home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 2 / Apprentice-Release2.iso / Source Code / Peter Lewis / PNL Libraries / TCPConnections.p < prev    next >
Encoding:
Text File  |  1994-08-27  |  16.1 KB  |  610 lines  |  [TEXT/PJMM]

  1. unit TCPConnections;
  2.  
  3. { TCPConnections © Peter Lewis, Oct 1991 }
  4.  
  5. interface
  6.  
  7.     uses
  8.         TCPStuff;
  9.  
  10.     const
  11.         tooManyConnections = -23099;
  12.  
  13.     const
  14.         any_connection = 0;    { Pass to GetConnectionEvent }
  15.         no_connection = -1;    { Guaranteed invalid connection }
  16.  
  17.     type
  18.         connectionIndex = longInt;
  19.         connectionEvent = (C_NoEvent, C_Found, C_SearchFailed, C_NameFound, C_NameSearchFailed,{}
  20.             C_Established, C_FailedToOpen, C_Closing, C_Closed, C_CharsAvailable, C_HeartBeat);
  21.         connectionEventRecord = record
  22.                 event: connectionEvent;
  23.                 connection: connectionIndex;
  24.                 tcpc: TCPConnectionPtr;
  25.                 dataptr: ptr;
  26.                 value: longInt;
  27.                 timedout: boolean;
  28.             end;
  29.  
  30.     function InitConnections: OSErr;
  31.     procedure CloseConnections;
  32.     procedure TerminateConnections;
  33.     function CanQuit: boolean;
  34. { After Terminate, keep calling GetConnectionEvent(any_connection,cer) until CanQuit is true, then Finish }
  35.     procedure FinishConnections;
  36.     procedure FinishEverything;  { Or just call FinishEverything }
  37.     function FindAddress (var cp: connectionIndex; hostName: str255; dataptr: univ ptr): OSErr;
  38.     function FindName (var cp: connectionIndex; hostIP: longInt; dataptr: univ ptr): OSErr;
  39.     procedure FindString (hostIP: longInt; var s: str255);
  40.     function NewPassiveConnection (var cp: connectionIndex; buffersize: longInt; localport: integer; remotehost: longInt; remoteport: integer; dataptr: univ ptr): OSErr;
  41.     function NewActiveConnection (var cp: connectionIndex; buffersize: longInt; localport: integer; remotehost: longInt; remoteport: integer; dataptr: univ ptr): OSErr;
  42.     procedure CloseConnection (cp: connectionIndex);
  43.     procedure AbortConnection (cp: connectionIndex); { Violently close connection }
  44.     function GetConnectionEvent (cp: connectionIndex; var cer: connectionEventRecord): boolean;
  45. { Pass any_connection for any event, otherwise cp specifies the event }
  46.     procedure SetDataPtr (cp: connectionIndex; dataptr: univ ptr);
  47.     procedure GetDataPtr (cp: connectionIndex; var dataptr: univ ptr);
  48.     procedure SetConnectionTimeout (cp: connectionIndex; timeout: longInt);
  49.     procedure GetConnectionTimeout (cp: connectionIndex; var timeout: longInt);
  50.     procedure GetConnectionTCPC (cp: connectionIndex; var tcpc: TCPConnectionPtr);
  51.     procedure SetHeartBeat (cp: connectionIndex; n: longInt); { Send C_HeartBeat every n ticks, 0 disables heartbeat }
  52.  
  53. implementation
  54.  
  55.     uses
  56.         DNR;
  57.  
  58.     const
  59.         TCPCMagic = 'TCPC';
  60.         TCPCBadMagic = 'badc';
  61.  
  62.     const  { Tuning parameters }
  63.         max_connections = 40;
  64.         TO_FindAddress = 40 * 60;
  65.         TO_FindName = 40 * 60;
  66.         TO_ActiveOpen = 20 * 60;
  67.         TO_Closing = longInt(2) * 60 * 60;
  68.         TO_PassiveOpen = longInt(10) * 365 * 24 * 3600 * 60;  { Ten years should be safe enough right? :-) }
  69.  
  70.     type
  71.         myHostInfo = record
  72.                 hi: hostInfo;
  73.                 done: signedByte;
  74.             end;
  75.         myHostInfoPtr = ^myHostInfo;
  76.         statusType = (CS_None, CS_Searching, CS_NameSearching, CS_Opening, CS_Established, CS_Closing);
  77.         connectionRecord = record
  78.                 magic: OSType;
  79.                 conmagic: longInt;
  80.                 tcpc: TCPConnectionPtr;
  81.                 status: statusType;
  82.                 dnrrp: DNRRecordPtr;
  83.                 closedone: boolean;
  84.                 timeout: longInt;
  85.                 dataptr: ptr;
  86.                 heartbeat: longInt; { Time for next heartbeat }
  87.                 period: longInt; { Ticks per heartbeat }
  88.             end;
  89.  
  90.     var
  91.         connections: array[1..max_connections] of connectionRecord;
  92.         connectionItem: connectionIndex;
  93.         connectionmagic: longInt;
  94.  
  95.     function ValidConnectionSafe (var cp: connectionIndex): boolean;
  96.         var
  97.             ocp: longInt;
  98.             vc: boolean;
  99.     begin
  100.         vc := false;
  101.         ocp := cp;
  102.         cp := cp mod (max_connections + 1);
  103.         if cp > 0 then
  104.             if connections[cp].magic = TCPCMagic then
  105.                 if connections[cp].conmagic = ocp then
  106.                     vc := true;
  107.         ValidConnectionSafe := vc;
  108.     end;
  109.  
  110.     function ValidConnection (var cp: connectionIndex): boolean;
  111.         var
  112.             vc: boolean;
  113.     begin
  114.         vc := ValidConnectionSafe(cp);
  115.         if not vc then
  116.             DebugStr('Invalid Connection');
  117.         ValidConnection := vc;
  118.     end;
  119.  
  120.     procedure SetDataPtr (cp: connectionIndex; dataptr: univ ptr);
  121.     begin
  122.         if ValidConnection(cp) then
  123.             connections[cp].dataptr := dataptr;
  124.     end;
  125.  
  126.     procedure GetDataPtr (cp: connectionIndex; var dataptr: univ ptr);
  127.     begin
  128.         if ValidConnectionSafe(cp) then
  129.             dataptr := connections[cp].dataptr
  130.         else
  131.             dataptr := nil;
  132.     end;
  133.  
  134.     procedure SetConnectionTimeout (cp: connectionIndex; timeout: longInt);
  135.     begin
  136.         if ValidConnection(cp) then
  137.             connections[cp].timeout := timeout;
  138.     end;
  139.  
  140.     procedure GetConnectionTimeout (cp: connectionIndex; var timeout: longInt);
  141.     begin
  142.         if ValidConnection(cp) then
  143.             timeout := connections[cp].timeout
  144.         else
  145.             timeout := -1;
  146.     end;
  147.  
  148.     procedure SetHeartBeat (cp: connectionIndex; n: longInt); { Send C_HeartBeat every n ticks }
  149.     begin
  150.         if ValidConnection(cp) then begin
  151.             if (n < 1) or (n = maxLongInt) then begin
  152.                 connections[cp].period := maxLongInt;
  153.                 connections[cp].heartbeat := maxLongInt;
  154.             end
  155.             else begin
  156.                 connections[cp].period := n;
  157.                 connections[cp].heartbeat := TickCount + n;
  158.             end;
  159.         end;
  160.     end;
  161.  
  162.     procedure GetConnectionTCPC (cp: connectionIndex; var tcpc: TCPConnectionPtr);
  163.     begin
  164.         if ValidConnectionSafe(cp) then
  165.             tcpc := connections[cp].tcpc
  166.         else
  167.             tcpc := nil;
  168.     end;
  169.  
  170.     function MyTCPState (con: TCPConnectionPtr): TCPStateType;
  171.     begin
  172.         if con = nil then
  173.             MyTCPState := T_Closed
  174.         else
  175.             MyTCPState := TCPState(con);
  176.     end;
  177.  
  178. {$S Init}
  179.     function InitConnections: OSErr;
  180.         var
  181.             oe, ooe: OSErr;
  182.             i: connectionIndex;
  183.     begin
  184.         for i := 1 to max_connections do
  185.             connections[i].magic := TCPCBadMagic;
  186.         connectionmagic := 0;
  187.         connectionItem := 1;
  188.         oe := TCPInit;
  189.         if oe = noErr then begin
  190.             oe := OpenResolver;
  191.             if oe <> noErr then
  192.                 TCPFinish;
  193.         end;
  194.         InitConnections := oe;
  195.     end;
  196.  
  197. {$S Term}
  198.     procedure TerminateConnections;
  199.         var
  200.             i: connectionIndex;
  201.             oe: OSErr;
  202.     begin
  203.         for i := 1 to max_connections do
  204.             with connections[i] do
  205.                 if magic = TCPCMagic then
  206.                     if (status = CS_Established) or (status = CS_Opening) or (status = CS_Closing) then
  207.                         if TCPState(tcpc) <> T_Closed then
  208.                             oe := TCPAbort(tcpc);
  209.     end;
  210.  
  211. {$S Term}
  212.     procedure CloseConnections;
  213.         var
  214.             i: connectionIndex;
  215.             oe: OSErr;
  216.     begin
  217.         for i := 1 to max_connections do
  218.             with connections[i] do
  219.                 if magic = TCPCMagic then
  220.                     if (status = CS_Established) or (status = CS_Opening) or (status = CS_Closing) then
  221.                         if TCPState(tcpc) <> T_Closed then
  222.                             oe := TCPClose(tcpc, nil);
  223.     end;
  224.  
  225. {$S Term}
  226.     function CanQuit: boolean;
  227.         var
  228.             i: connectionIndex;
  229.     begin
  230.         CanQuit := true;
  231.         for i := 1 to max_connections do
  232.             if connections[i].magic = TCPCMagic then
  233.                 CanQuit := false;
  234.     end;
  235.  
  236. {$S Term}
  237.     procedure FinishConnections;
  238.     begin
  239.         CloseResolver;
  240.         TCPFinish;
  241.     end;
  242.  
  243. {$S Term}
  244.     procedure FinishEverything;
  245.         var
  246.             cer: connectionEventRecord;
  247.             dummy: boolean;
  248.             er: eventrecord;
  249.             oe: OSErr;
  250.     begin
  251.         TerminateConnections;
  252.         while not CanQuit do begin
  253.             if GetConnectionEvent(any_connection, cer) then begin
  254.                 dummy := WaitNextEvent(everyEvent, er, 0, nil);
  255.             end
  256.             else
  257.                 dummy := WaitNextEvent(everyEvent, er, 5, nil);
  258.         end;
  259.         FinishConnections;
  260.     end;
  261.  
  262. {$S}
  263.     function CreateConnection (var cp: connectionIndex; dp: ptr): OSErr;
  264.         var
  265.             ts: TCPStateType;
  266.             ce: connectionEvent;
  267.     begin
  268.         connectionmagic := connectionmagic + max_connections + 1;
  269.         cp := 1;
  270.         while (connections[cp].magic = TCPCMagic) and (cp < max_connections) do
  271.             cp := cp + 1;
  272.         with connections[cp] do begin
  273.             if magic = TCPCMagic then
  274.                 CreateConnection := tooManyConnections
  275.             else begin
  276.                 magic := TCPCMagic;
  277.                 conmagic := cp + connectionmagic;
  278.                 closedone := false;
  279.                 tcpc := nil;
  280.                 status := CS_None;
  281.                 dnrrp := nil;
  282.                 timeout := maxlongInt;
  283.                 dataptr := dp;
  284.                 period := maxLongInt;
  285.                 heartbeat := maxLongInt;
  286.                 CreateConnection := noErr;
  287.                 cp := cp + connectionmagic;
  288.  
  289.             end;
  290.         end;
  291.     end;
  292.  
  293.     procedure DestroyConnection (var cp: connectionIndex);
  294.     begin
  295.         if ValidConnection(cp) then
  296.             connections[cp].magic := TCPCBadMagic;
  297.         cp := -1;
  298.     end;
  299.  
  300.     function FindAddress (var cp: connectionIndex; hostName: str255; dataptr: univ ptr): OSErr;
  301.         var
  302.             oe: OSErr;
  303.             cpi: connectionIndex;
  304.     begin
  305.         oe := CreateConnection(cp, dataptr);
  306.         if oe = noErr then begin
  307.             cpi := cp;
  308.             if ValidConnection(cpi) then begin
  309.                 with connections[cpi] do begin
  310.                     dnrrp := DNRRecordPtr(NewPtr(SizeOf(DNRRecord)));
  311.                     if dnrrp = nil then
  312.                         oe := memFullErr
  313.                     else begin
  314.                         DNRNameToAddr(hostName, dnrrp, nil);
  315.                         timeout := TickCount + TO_FindAddress;
  316.                         status := CS_Searching;
  317.                     end;
  318.                 end;
  319.             end;
  320.             if oe <> noErr then begin
  321.                 DestroyConnection(cp);
  322.             end;
  323.         end;
  324.         FindAddress := oe;
  325.     end;
  326.  
  327.     procedure FindString (hostIP: longInt; var s: str255);
  328.     begin
  329.         AddrToStr(hostIP, s);
  330.     end;
  331.  
  332.     function FindName (var cp: connectionIndex; hostIP: longInt; dataptr: univ ptr): OSErr;
  333.         var
  334.             oe: OSErr;
  335.             cpi: connectionIndex;
  336.     begin
  337.         oe := CreateConnection(cp, dataptr);
  338.         if oe = noErr then begin
  339.             cpi := cp;
  340.             if ValidConnection(cpi) then begin
  341.                 with connections[cpi] do begin
  342.                     dnrrp := DNRRecordPtr(NewPtr(SizeOf(DNRRecord)));
  343.                     if dnrrp = nil then
  344.                         oe := memFullErr
  345.                     else begin
  346.                         DNRAddrToName(hostIP, dnrrp, nil);
  347.                         timeout := TickCount + TO_FindName;
  348.                         status := CS_NameSearching;
  349.                     end;
  350.                 end;
  351.             end;
  352.             if oe <> noErr then begin
  353.                 DestroyConnection(cp);
  354.             end;
  355.         end;
  356.         FindName := oe;
  357.     end;
  358.  
  359.     function NewPassiveConnection (var cp: connectionIndex; buffersize: longInt; localport: integer; remotehost: longInt; remoteport: integer; dataptr: univ ptr): OSErr;
  360.         var
  361.             oe: OSErr;
  362.             cpi: connectionIndex;
  363.     begin
  364.         oe := CreateConnection(cp, dataptr);
  365.         if oe = noErr then begin
  366.             cpi := cp;
  367.             if ValidConnection(cpi) then
  368.                 with connections[cpi] do begin
  369.                     oe := TCPPassiveOpen(tcpc, buffersize, localPort, remotehost, remoteport, nil);
  370.                     timeout := TickCount + TO_PassiveOpen;
  371.                     status := CS_Opening;
  372.                     if oe <> noErr then
  373.                         DestroyConnection(cp);
  374.                 end;
  375.         end;
  376.         NewPassiveConnection := oe;
  377.     end;
  378.  
  379.     function NewActiveConnection (var cp: connectionIndex; buffersize: longInt; localport: integer; remotehost: longInt; remoteport: integer; dataptr: univ ptr): OSErr;
  380.         var
  381.             oe: OSErr;
  382.             cpi: connectionIndex;
  383.     begin
  384.         oe := CreateConnection(cp, dataptr);
  385.         if oe = noErr then begin
  386.             cpi := cp;
  387.             if ValidConnection(cpi) then
  388.                 with connections[cpi] do begin
  389.                     oe := TCPActiveOpen(tcpc, buffersize, localport, remotehost, remoteport, nil);
  390.                     timeout := TickCount + TO_ActiveOpen;
  391.                     status := CS_Opening;
  392.                     if oe <> noErr then
  393.                         DestroyConnection(cp);
  394.                 end;
  395.         end;
  396.         NewActiveConnection := oe;
  397.     end;
  398.  
  399.     procedure CloseConnection (cp: connectionIndex);
  400.         var
  401.             oe: OSErr;
  402.     begin
  403.         if ValidConnection(cp) then
  404.             with connections[cp] do begin
  405.                 if not closedone then begin
  406.                     if MyTCPState(tcpc) <> T_Closed then begin
  407.                         oe := TCPClose(tcpc, nil);
  408.                     end;
  409.                     closedone := true;
  410.                 end;
  411.                 status := CS_Closing;
  412.                 timeout := TickCount + TO_Closing;
  413.             end;
  414.     end;
  415.  
  416.     procedure AbortConnection (cp: connectionIndex);
  417.         var
  418.             oe: OSErr;
  419.     begin
  420.         if ValidConnection(cp) then
  421.             with connections[cp] do begin
  422.                 if MyTCPState(tcpc) <> T_Closed then
  423.                     oe := TCPAbort(tcpc);
  424.                 status := CS_Closing;
  425.                 timeout := TickCount + TO_Closing;
  426.             end;
  427.     end;
  428.  
  429.     function GetConnectionEvent (cp: connectionIndex; var cer: connectionEventRecord): boolean;
  430.         procedure HandleConnection (cp: connectionIndex);
  431.             var
  432.                 oe: OSErr;
  433.                 dummysp: stringPtr;
  434.                 l: integer;
  435.                 rcp: connectionIndex;
  436.         begin
  437.             with connections[cp] do begin
  438.                 rcp := conmagic;
  439.                 cer.connection := rcp;
  440.                 cer.tcpc := tcpc;
  441.                 cer.dataptr := dataptr;
  442.                 cer.timedout := false;
  443.                 case status of
  444.                     CS_NameSearching: 
  445.                         with dnrrp^ do begin
  446.                             if ioResult <> inProgress then begin
  447.                                 if ioResult = noErr then begin
  448.                                     cer.event := C_NameFound;
  449.                                     stringHandle(cer.value) := NewString(name);
  450.                                 end
  451.                                 else begin
  452.                                     cer.event := C_NameSearchFailed;
  453.                                     cer.value := ioResult;
  454.                                 end
  455.                             end
  456.                             else if TickCount > timeout then begin
  457.                                 cer.event := C_NameSearchFailed;
  458.                                 cer.value := 1;
  459.                                 cer.timedout := true;
  460.                             end;
  461.                             if cer.event <> C_NoEvent then begin  { Destroy the connection now }
  462.                                 if ioResult <> inProgress then  { If we timed out, then we'll just have to abandon this block.  Oh well }
  463.                                     DisposPtr(ptr(dnrrp));
  464.                                 dnrrp := nil;
  465.                                 DestroyConnection(rcp);
  466.                             end; {if}
  467.                         end; {with}
  468.                     CS_Searching: 
  469.                         with dnrrp^ do begin
  470.                             if ioResult = noErr then begin
  471.                                 cer.event := C_Found;
  472.                                 cer.value := addr;
  473.                             end
  474.                             else if ioResult <> inProgress then begin
  475.                                 cer.event := C_SearchFailed;
  476.                                 cer.value := ioResult;
  477.                             end
  478.                             else if TickCount > timeout then begin
  479.                                 cer.event := C_SearchFailed;
  480.                                 cer.value := 1;
  481.                                 cer.timedout := true;
  482.                             end;
  483.                             if cer.event <> C_NoEvent then begin  { Destroy the connection now }
  484.                                 if ioResult <> inProgress then  { If we timed out, then we'll just have to abandon this block.  Oh well }
  485.                                     DisposPtr(ptr(dnrrp));
  486.                                 dnrrp := nil;
  487.                                 DestroyConnection(rcp);
  488.                             end; {if}
  489.                         end; {with}
  490.                     CS_Opening:  begin
  491.                         case MyTCPState(tcpc) of
  492.                             T_WaitingForOpen, T_Opening, T_Listening: 
  493.                                 if TickCount > timeout then begin
  494.                                     CloseConnection(rcp);
  495.                                     cer.event := C_FailedToOpen;
  496.                                     cer.timedout := true;
  497.                                 end;
  498.                             T_Established:  begin
  499.                                 cer.event := C_Established;
  500.                                 status := CS_Established;
  501.                                 timeout := maxLongInt;
  502.                             end;
  503.                             T_PleaseClose, T_Closing:  begin
  504.                                 CloseConnection(rcp);
  505.                                 cer.value := 1;
  506.                                 cer.event := C_FailedToOpen;
  507.                                 timeout := TickCount + TO_Closing;
  508.                             end;
  509.                             T_Closed:  begin
  510.                                 status := CS_Closing;
  511.                                 cer.value := 2;
  512.                                 cer.event := C_FailedToOpen;
  513.                                 timeout := TickCount + TO_Closing;
  514.                             end;
  515.                             otherwise
  516.                                 ;
  517.                         end; {case }
  518.                     end;
  519.                     CS_Established:  begin
  520.                         case MyTCPState(tcpc) of
  521.                             T_Established:  begin
  522.                                 cer.value := TCPCharsAvailable(tcpc);
  523.                                 if cer.value > 0 then
  524.                                     cer.event := C_CharsAvailable;
  525.                             end;
  526.                             T_PleaseClose, T_Closing:  begin
  527.                                 cer.value := TCPCharsAvailable(tcpc);
  528.                                 if cer.value > 0 then
  529.                                     cer.event := C_CharsAvailable
  530.                                 else begin
  531. {    CloseConnection(rcp);}
  532.                                     status := CS_Closing;
  533.                                     cer.event := C_Closing;
  534.                                     timeout := TickCount + TO_Closing;
  535.                                 end;
  536.                             end;
  537.  
  538.                             T_Closed:  begin
  539.                                 status := CS_Closing;
  540.                                 cer.event := C_Closing;
  541.                                 timeout := TickCount + TO_Closing;
  542.                             end;
  543.                             otherwise
  544.                                 ;
  545.                         end;
  546.                     end;
  547.                     CS_Closing:  begin
  548.                         case MyTCPState(tcpc) of
  549.                             T_WaitingForOpen, T_Opening, T_Listening: 
  550.                                 ;
  551. {DebugStr('Strange State 2')}
  552.                             T_PleaseClose, T_Closing, T_Established:  begin
  553.                                 cer.value := TCPCharsAvailable(tcpc);
  554.                                 if cer.value > 0 then
  555.                                     cer.event := C_CharsAvailable
  556.                                 else if TickCount > timeout then begin
  557.                                     cer.event := C_Closed;
  558.                                     if tcpc <> nil then begin
  559. {DebugStr('Closing timeout, call Abort;g');}
  560.                                         oe := TCPAbort(tcpc);
  561.                                         oe := TCPRelease(tcpc);
  562.                                     end;
  563.                                     cer.timedout := true;
  564.                                     DestroyConnection(rcp);
  565.                                 end;
  566.                             end;
  567.                             T_Closed:  begin
  568.                                 cer.event := C_Closed;
  569.                                 if tcpc <> nil then
  570.                                     oe := TCPRelease(tcpc);
  571.                                 DestroyConnection(rcp);
  572.                             end;
  573.                             otherwise
  574.                                 ;
  575.                         end;
  576.                     end;
  577.                     otherwise
  578.                         ;
  579.                 end;
  580.  
  581.                 if (cer.event = C_NoEvent) & (TickCount > heartbeat) then begin
  582.                     cer.event := C_HeartBeat;
  583.                     heartbeat := TickCount + period;
  584.                 end;
  585.             end;{with}
  586.         end;{HandleConnection}
  587.         var
  588.             oci: connectionIndex;
  589.     begin
  590.         cer.event := C_NoEvent;
  591.         if cp <> any_connection then begin
  592.             if ValidConnection(cp) then
  593.                 HandleConnection(cp);
  594.         end
  595.         else begin
  596.             oci := connectionItem;
  597.             repeat
  598.                 if connections[connectionItem].magic = TCPCMagic then begin
  599.                     HandleConnection(connectionItem);
  600.                 end;{if}
  601.                 if connectionItem = max_connections then
  602.                     connectionItem := 1
  603.                 else
  604.                     connectionItem := connectionItem + 1;
  605.             until (oci = connectionItem) or (cer.event <> C_NoEvent);
  606.         end;{if}
  607.         GetConnectionEvent := cer.event <> C_NoEvent;
  608.     end;{GetConnectionEvent}
  609.  
  610. end.